<?php

include_once('nano.php');
include_once('nanolexer.php');

$nanoerrors = array();
$currentoffset = 0;
$labels = array();
$waitlabels = array();

$errors = array(
	'NANO_SYNTAX_ERROR_UNEXPECTED' => 'Syntax error : unexpected \'%val%\'',
	'NANO_SYNTAX_ERROR_MISSING' => 'Syntax error : missing \'%val%\'',
	'NANO_ILLEGAL_INSTRUCTION' => 'Syntax error : illegal instruction',
	'NANO_ERROR_UNDEFINED' => 'Undefined label : \'%val%\'',
	'NANO_OUT_OF_RANGE' => 'Out of range : \'%val%\'');

$syntax = array(
	'null' => array('missing' => 'internal error'),
	'zero' => array('label' => 'label', 'inst' => 'inst', 'missing' => 'label or instruction'),
	'label' => array('special' => '', ':' => 'stop', 'missing' => ':'),
	'stop' => array('eof' => ''),
	'inst' => array('flag' => 'spec', 'reg' => 'spec', 'special' => '', '(' => 'ind1', '+' => 'rel1', '-' => 'rel1', '~' => 'negint1', 'int' => 'spec', 'label' => 'spec', 'eof' => ''),
	'spec' => array('special' => '', ',' => 'inst2', 'eof' => ''),
	'ind1' => array('label' => 'closeind1', 'int' => 'closeind1', 'reg' => 'closeind1', 'special' => '', '+' => 'indrel1', '-' => 'indrel1', '~' => 'indrel1', 'missing' => 'register or adress or label'),
	'rel1' => array('int' => 'spec', 'missing' => 'adress'),
	'negint1' => array('int' => 'spec', 'missing' => 'number'),
	'closeind1' => array('special' => '', ')' => 'spec', 'missing' => ')'),
	'indrel1' => array('int' => 'closeind1', 'missing' => 'adress'),
	'inst2' => array('reg' => 'stop', 'special' => '', '(' => 'ind2', '+' => 'rel2', '-' => 'rel2', '~' => 'negint2', 'int' => 'stop', 'label' => 'stop', 'missing' => 'register or adress or label'),
	'ind2' => array('label' => 'closeind2', 'int' => 'closeind2', 'reg' => 'closeind2', 'special' => '', '+' => 'indrel2', '-' => 'indrel2', '~' => 'indrel2', 'missing' => 'register or adress or label'),
	'rel2' => array('int' => 'stop', 'missing' => 'adress'),
	'negint2' => array('int' => 'stop', 'missing' => 'number'),
	'closeind2' => array('special' => '', ')' => 'stop', 'missing' => ')'),
	'indrel2' => array('int' => 'closeind2', 'missing' => 'adress'));

$patterns = array( /* 79 */
	'inst' => 'I',
	'label' => 'L',
	'reg' => 'R',
	'special' => array('(' => 'M', ')' => '', '+' => 'r', '-' => 'r', '~' => '', ',' => '', ':' => ''),
	'flag' => 'F',
	'int' => 'A',
	'mov' => array('IRR' => 1, 'IRA' => 2, 'IRMR' => 3, 'IRMA' => 4, 'IRMrA' => 5, 'IRML' => 6, 'IMRR' => 7, 'IMAR' => 8, 'IMrAR' => 9, 'IRrA' => 76, 'IRL' => 77),
	'add' => array('IRR' => 10, 'IRMR' => 11, 'IRA' => 12, 'IRMA' => 13, 'IRMrA' => 14),
	'sub' => array('IRR' => 15, 'IRMR' => 16, 'IRA' => 17, 'IRMA' => 18, 'IRMrA' => 19),
	'mul' => array('IRR' => 20, 'IRMR' => 21, 'IRA' => 22, 'IRMA' => 23, 'IRMrA' => 24),
	'inc' => array('IR' => 25, 'IMR' => 26),
	'dec' => array('IR' => 27, 'IMR' => 28),
	'or'  => array('IRR' => 29, 'IRMR' => 30, 'IRA' => 31, 'IRMA' => 32, 'IRMrA' => 33),
	'and' => array('IRR' => 34, 'IRMR' => 35, 'IRA' => 36, 'IRMA' => 37, 'IRMrA' => 38),
	'xor' => array('IRR' => 39, 'IRMR' => 40, 'IRA' => 41, 'IRMA' => 42, 'IRMrA' => 43),
	'push' => array('IR' => 44),
	'pop' => array('IR' => 45),
	'call' => array('IR' => 46, 'IMR' => 47, 'IA' => 48, 'IrA' => 49, 'IL' => 50, 'IMA' => 51, 'IMrA' => 52, 'IFR' => 53, 'IFMR' => 54, 'IFA' => 55, 'IFrA' => 56, 'IFL' => 57, 'IFMA' => 58, 'IFMrA' => 59),
	'ret' => array('I' => 60, 'IF' => 61),
	'jmp' => array('IA' => 62, 'IrA' => 63, 'IL' => 64, 'IFA' => 65, 'IFrA' => 66, 'IFL' => 67, 'IR' => 71, 'IFR' => 72),
	'cmp' => array('IRR' => 68, 'IRA' => 69),
	'test' => array('IR' => 70),
	'djnz' => array('IA' => 73, 'IrA' => 74, 'IL' => 75),
	'shr' => array('IR' => 78),
	'shl' => array('IR' => 79));// DIV

	
$functions = array(
	'createthread' => 0,
	'debugdispreg' => 1);

function NanoMakeProgram($code, $addr)
{
	$header = 'nano=1:addr=' . $addr . '::';
	return NanoParser($code, $header);
}

function NanoParser($str, $header)
{
	global $nanoerrors;
	global $currentoffset;
	$nanoerrors = array();
	$currentoffset = 0;
	$hex = array();
	
	$lines = NanoLexer($str);
	foreach($lines as $key => $l)
	{
		$pattern = NanoCheckSyntax($l, $key);
		if($pattern == false) continue;
		$linehex = NanoCheckPattern($l, $key, $pattern);
		if($linehex !== null)
		{
			$hex[] = $linehex;
			$currentoffset++;
		}
	}
	NanoResolveLabel($hex);
	$prgm = NanoCompilePrgm($hex);
	$hexprgm = $header . NanoCompilePrgm($hex, true);
	return array('success' => empty($nanoerrors), 'errors' => (empty($nanoerrors)?array('No error'):$nanoerrors), 'hex' => $hexprgm);
}

function NanoCompilePrgm($hex, $bhex = false)
{
	$prgm = '';
	foreach($hex as $h)
	{
		$prgm .= ($bhex?'0x' . dechex($h):$h);
		$prgm .= ':';
	}
	$prgm = rtrim($prgm, ':');
	return $prgm;
}

function NanoCheckSyntax($line, $n)
{
	global $syntax;
	global $patterns;
	$pattern = '';
	$count = count($line);
	$comment = false;
	$allowed = $syntax['zero'];
	for($i = 0; $i < $count; $i++)
	{
		$type = $line[$i]['type'];
		$val = $line[$i]['val'];
		if($type == 'special')
		{
			if($val == ';') { $comment = true; break; }
			$p = $patterns[$type][$val];
		}
		else
			$p = $patterns[$type];
		$pattern .= $p;
		
		if(array_key_exists($type, $allowed))
		{
			if($type == 'special')
			{
				if(!array_key_exists($val, $allowed))
				{
					NanoAddError('NANO_SYNTAX_ERROR_UNEXPECTED', $n, $val);
					break;
				}
				$allowed = $syntax[$allowed[$val]];
			}
			else
				$allowed = $syntax[$allowed[$type]];
		}
		else
		{
			NanoAddError('NANO_SYNTAX_ERROR_UNEXPECTED', $n, $val);
			break;
		}
	}
	if($comment) return ($pattern == ''?false:$pattern);
	if($i != $count) return false;
	if(array_key_exists('eof', $allowed)) return $pattern;
	NanoAddError('NANO_SYNTAX_ERROR_MISSING', $n, $allowed['missing']);
	return false;
}

function NanoCheckPattern($line, $n, $p)
{
	global $patterns;
	if($p == 'L')
	{
		NanoRegisterLabel($line[0]['val']);
		return null;
	}
	else
	{
		$inst = $line[0]['val'];
		if(array_key_exists($p, $patterns[$inst]))
		{
			$hex = $patterns[$inst][$p];
			$hex <<= 24;
			
			$low = 0;
			$addr = 0;
			$val = NanoGetSignificantValues($line, $n);
			if($val === false) return 0;
			$count = count($val);
			for($i = 0; $i < $count; $i++)
			{
				switch($val[$i]['type'])
				{
				case 'label':
					NanoWaitLabel($val[$i]['val'], $n);
					break;
				
				case 'reg':
				case 'flag':
					$low <<= 4;
					$low |= NanoGetOpCode($val[$i]);
					break;
				
				case 'int':
					$addr = $val[$i]['val'];
					break;
				}
			}
			$hex |= ($low << 16)|$addr;
			return $hex;
		}
		else
		{
			NanoAddError('NANO_ILLEGAL_INSTRUCTION', $n);
			return 0;
		}
	}
}

function NanoGetOpCode($e)
{
	global $flags;
	global $registers;
	switch($e['type'])
	{
	case 'flag': return $flags[$e['val']]['op']; break;
	case 'reg': return $registers[$e['val']]['op']; break;
	case 'int': return $e['val']; break;
	}
	return null;
}

function NanoGetSignificantValues($line, $n)
{
	$count = count($line);
	$val = array();
	$sign = 0;
	for($i = 0; $i < $count; $i++)
	{
		switch($line[$i]['type'])
		{
		case 'flag':
		case 'label':
		case 'int':
			if(abs($line[$i]['val']) > 0xFFFF)
			{
				NanoAddError('NANO_OUT_OF_RANGE', $n, $line[$i]['val']);
				return false;
			}
		case 'reg':
			if($sign)
			{
				$val[] = array('type' => 'int', 'val' => ($sign*$line[$i]['val']&0xFFFF));
				$sign = 0;
			}
			else
				$val[] = $line[$i];
			break;
		case 'special':
			if($line[$i]['val'] == '+') $sign = 1;
			else if($line[$i]['val'] == '-' || $line[$i]['val'] == '~') $sign = -1;
			break;
		}
	}
	return $val;
}

function NanoRegisterLabel($l)
{
	global $labels;
	global $currentoffset;
	
	$labels[$l] = $currentoffset;
}

function NanoWaitLabel($label, $line)
{
	global $waitlabels;
	global $currentoffset;
	$waitlabels[] = array($label, $line, $currentoffset);
}

function NanoResolveLabel(&$hex)
{
	global $waitlabels;
	global $labels;
	global $functions;
	foreach($waitlabels as $l)
	{
		if(!isset($labels[$l[0]]))
		{
			if(!isset($functions[$l[0]]))
			{
				NanoAddError('NANO_ERROR_UNDEFINED', $l[1], $l[0]);
				continue;
			}
			else
				$addr = $functions[$l[0]];
		}
		else
		{
			$addr = $labels[$l[0]] - $l[2];
			$hex[$l[2]] -= 0x01000000;
		}
		$hex[$l[2]] |= $addr&0xFFFF;
	}
}

function NanoAddError($error, $line, $val = null)
{
	global $nanoerrors;
	global $errors;
	if($val != null) $nanoerrors[] = 'Line ' . ($line+1) . ' : ' . str_replace('%val%', $val, $errors[$error]);
	else			 $nanoerrors[] = 'Line ' . ($line+1) . ' : ' . $errors[$error];
}

?>